﻿#include "FITKGraphCommons.h"

// C++
#include <cmath>

// VTK
#include <vtkTransform.h>
#include <vtkDataObject.h>
#include <vtkDataSet.h>
#include <vtkTransformFilter.h>
#include <vtkPointSet.h>
#include <vtkCellData.h>
#include <vtkCell.h>
#include <vtkDataArray.h>
#include <vtkMath.h>
#include <vtkTexture.h>
#include <vtkDoubleArray.h>
#include <vtkImageData.h>
#include <vtkPolyData.h>
#include <vtkPointData.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPlane.h>
#include <vtkPolygon.h>
#include <vtkMaskPoints.h>
#include <vtkRenderer.h>
#include <vtkCamera.h>
#include <vtkInteractorObserver.h>
#include <vtkAbstractCellLocator.h>
#include <vtkGenericCell.h>

#ifndef PLANE_TOL
#define PLANE_TOL 1.0e-06
#endif // !PLANE_TOL

// Data
#include "FITK_Interface/FITKInterfaceModel/FITKAssInstance.h"

namespace Exchange
{
    // Initialize color list.
    //@{
    QList<QColor>FITKGraphCommons::s_colorList =
    {
        QColor(69, 139, 116), QColor(245, 245, 220), QColor(187, 58, 58),
        QColor(0, 94, 157), QColor(193, 205, 205), QColor(222, 184, 135),
        QColor(177, 198, 237), QColor(139, 136, 120), QColor(233, 150, 122),
        QColor(189, 183, 107), QColor(139, 10, 80), QColor(49, 58, 151),
        QColor(202, 217, 187), QColor(238, 201, 0), QColor(173, 216, 230),
        QColor(110, 123, 139), QColor(139, 71, 137), QColor(238, 232, 170),
        QColor(184, 206, 198), QColor(184, 184, 219), QColor(206, 164, 107),
        QColor(106, 90, 205), QColor(238, 233, 233), QColor(0, 51, 102)
    };
    //@}

    // Initialize colors;
    //@{
    QColor FITKGraphCommons::s_postMeshColor = Qt::green;
    QColor FITKGraphCommons::s_highlightColor = Qt::red;
    QColor FITKGraphCommons::s_preHighlightColor = QColor(255, 120, 20);
    QColor FITKGraphCommons::s_defaultMeshColor = QColor(70, 170, 70);
    QColor FITKGraphCommons::s_defaultGeoMeshColor = QColor(5, 255, 255);
    QColor FITKGraphCommons::s_validColor = QColor(100, 200, 165);
    QColor FITKGraphCommons::s_invalidColor = QColor(215, 175, 0);
    QColor FITKGraphCommons::s_notFoundColor = QColor(165, 0, 0);
    QColor FITKGraphCommons::s_noneColor = QColor(165, 165, 165);
    QColor FITKGraphCommons::s_shellFrontColor = QColor(255, 150, 90);
    QColor FITKGraphCommons::s_shellBackColor = QColor(110, 20, 160);
    QColor FITKGraphCommons::s_forceLoadColor = Qt::yellow;
    QColor FITKGraphCommons::s_momentLoadColor = QColor(130, 130, 230);
    QColor FITKGraphCommons::s_pressureLoadColor = QColor(238, 130, 238);
    QColor FITKGraphCommons::s_loadHighlightColor = QColor(255, 120, 20);
    QColor FITKGraphCommons::s_refNodeColor = Qt::yellow;
    QColor FITKGraphCommons::s_refNodeHighlightColor = Qt::red;
    QColor FITKGraphCommons::s_dispBCColor = QColor(255, 130, 0);
    QColor FITKGraphCommons::s_rotateBCColor = Qt::blue;
    QColor FITKGraphCommons::s_velBCColor = QColor(255, 160, 100);
    QColor FITKGraphCommons::s_angleVelBCColor = QColor(255, 0, 255);
    QColor FITKGraphCommons::s_constraintColor = Qt::yellow;
    QColor FITKGraphCommons::s_constCtrlPointColor = QColor(255, 180, 190);
    QColor FITKGraphCommons::s_constraintHighlightColor = QColor(255, 120, 20);
    QColor FITKGraphCommons::s_constraintLocColor1 = QColor(255, 0, 0);
    QColor FITKGraphCommons::s_constraintLocColor2 = QColor(255, 0, 255);
    QColor FITKGraphCommons::s_interactionHighlightColor = Qt::red;
    QColor FITKGraphCommons::s_intertiaColor = Qt::green;
    QColor FITKGraphCommons::s_intertiaHighlightColor = QColor(255, 120, 20);
    QColor FITKGraphCommons::s_coordinateColor = Qt::yellow;
    QColor FITKGraphCommons::s_coordinateHighlightColor = Qt::red;
    QColor FITKGraphCommons::s_wireColor = QColor(30, 145, 255);
    QColor FITKGraphCommons::s_wireVertexColor = Qt::yellow;
    QColor FITKGraphCommons::s_wireHighlightColor = Qt::red;
    QColor FITKGraphCommons::s_springColor = QColor(200, 0, 200);
    QColor FITKGraphCommons::s_springHighlightColor = QColor(255, 130, 15);
    QColor FITKGraphCommons::s_connStartPtColor = QColor(255, 120, 20);
    QColor FITKGraphCommons::s_connEndPtColor = Qt::yellow;
    QColor FITKGraphCommons::s_connLineColor = QColor(255, 200, 15);
    QColor FITKGraphCommons::s_connHighlightColor = QColor(255, 120, 20);
    QColor FITKGraphCommons::s_connLineHighlightColor = Qt::red;
    QColor FITKGraphCommons::s_velFieldColor = QColor(255, 160, 100);
    QColor FITKGraphCommons::s_velFieldHighlightColor = QColor(255, 130, 0);
    QColor FITKGraphCommons::s_tempFieldColor = Qt::yellow;
    QColor FITKGraphCommons::s_tempFieldHighlightColor = QColor(255, 130, 0);
    QColor FITKGraphCommons::s_beamOrientColor_t = Qt::red;
    QColor FITKGraphCommons::s_beamOrientColor_1 = QColor(0, 255, 255);
    QColor FITKGraphCommons::s_beamOrientColor_2 = Qt::yellow;
    QColor FITKGraphCommons::s_connCsysColor = QColor(255, 200, 15);
    QColor FITKGraphCommons::s_geometryPartColor = QColor(255, 255, 255);
    QColor FITKGraphCommons::s_geometryInstanceColor = QColor(50, 170, 255);
    QColor FITKGraphCommons::s_geoPartForMeshColorInvalid = QColor(220, 110, 0);
    QColor FITKGraphCommons::s_geoPartForMeshColorSweep = Qt::yellow;
    QColor FITKGraphCommons::s_geoPartForMeshColorStructure = Qt::green;
    QColor FITKGraphCommons::s_geoPartForMeshColorFree = QColor(255, 190, 200);
    QColor FITKGraphCommons::s_geoPartForMeshColorBtmToUp = QColor(255, 230, 200);
    QColor FITKGraphCommons::s_sketchMainLineColor = QColor(0, 60, 200);
    QColor FITKGraphCommons::s_sketchSubLineColor = QColor(80, 80, 80);
    QColor FITKGraphCommons::s_sketchAxisColor = QColor(150, 100, 255);
    QColor FITKGraphCommons::s_sketchPreviewColor = Qt::red;
    QColor FITKGraphCommons::s_sketchColor = Qt::yellow;
    QColor FITKGraphCommons::s_featurePreviewColor = QColor(0, 190, 255);
    //@}
    
    // Initialize size.
    //@{
    double FITKGraphCommons::s_highlightPointSize = 10.;
    double FITKGraphCommons::s_highlightLineWidth = 3.;
    //@}

    // Initialize properties.
    //@{
    double FITKGraphCommons::s_transparency = 0.8;
    int FITKGraphCommons::s_pixelSnipGridPoint = 10;
    int FITKGraphCommons::s_pixelSnipSketchPoint = 25;
    int FITKGraphCommons::s_pixelSnipSketchLine = 20;
    //@}

    // Initialize polygon offsets.
    //@{
    double FITKGraphCommons::s_geomPolyOffset = 5.;
    double FITKGraphCommons::s_meshPolyOffset = -5.;
    //@}

    QColor FITKGraphCommons::GetColorByIndex(int index)
    {
        // Default color list.
        return s_colorList[index % s_colorList.count()];
    }

    void FITKGraphCommons::QColorToDouble3(QColor color, double* rgbf)
    {
        rgbf[0] = color.redF();
        rgbf[1] = color.greenF();
        rgbf[2] = color.blueF();
    }

    void FITKGraphCommons::QColorToInt3(QColor color, int* rgb)
    {
        rgb[0] = color.red();
        rgb[1] = color.green();
        rgb[2] = color.blue();
    }

    bool FITKGraphCommons::FuzzyCompare(double a, double b, double tol)
    {
        return qAbs(a - b) < tol;
    }

    void FITKGraphCommons::GetTransformedDataSet(vtkTransform* transform, vtkDataObject* input, vtkDataSet* output)
    {
        if (!transform || !input || !output)
        {
            return;
        }

        // Create a transform filter.
        vtkTransformFilter* transFilter = vtkTransformFilter::New();
        transFilter->SetTransform(transform);
        transFilter->SetInputData(input);
        transFilter->Update();

        output->DeepCopy(transFilter->GetOutput());
        transFilter->Delete();
    }

    void FITKGraphCommons::GetInstanceTransform(Interface::FITKAbstractAssInstance* instance, vtkTransform* transform)
    {
        if (!instance || !transform)
        {
            return;
        }

        // Create a new one.
        transform->DeepCopy(vtkSmartPointer<vtkTransform>::New());
        vtkMatrix4x4* vtkMat44 = transform->GetMatrix();

        // Custom type instance data.
        Interface::FITKAssemblyInstance* instanceCustom = dynamic_cast<Interface::FITKAssemblyInstance*>(instance);
        if (!instanceCustom)
        {
            return;
        }

        if (instanceCustom->hasTransformTool())
        {
            // Get the matrix 4x4.
            //@{
            double mat44[4][4];
            instanceCustom->getMatrix4X4(mat44);
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    vtkMat44->SetElement(i, j, mat44[i][j]);
                }
            }
            //@}
        }
        else
        {
            // Get the transformation directly.
            //@{
            double trans3[3], axisPt1[3], axisPt2[3], angle;
            instanceCustom->getTransfom(trans3);
            instanceCustom->getRotateAxis(axisPt1, axisPt2, &angle);

            transform->Translate(axisPt1);
            transform->RotateWXYZ(angle, axisPt2[0] - axisPt1[0], axisPt2[1] - axisPt1[1], axisPt2[2] - axisPt1[2]);
            transform->Translate(-axisPt1[0], -axisPt1[1], -axisPt1[2]);
            transform->Translate(trans3);
            //@}
        }
    }

    void FITKGraphCommons::RemoveCellNormals(vtkDataSet* dataset, bool deleteArray)
    {
        // Check the input.
        if (!dataset)
        {
            return;
        }

        // Check the cell data.
        vtkCellData* cellData = dataset->GetCellData();
        if (!cellData)
        {
            return;
        }

        // cellData->SetActiveAttribute(vtkDataSetAttributes::NORMALS, false);

        // Get the normals array.
        vtkDataArray* normals = cellData->GetNormals();
        if (!normals)
        {
            return;
        }

        cellData->SetNormals(nullptr);

        if (deleteArray)
        {
            normals->Delete();
        }
    }

    void FITKGraphCommons::GetCellCenter(vtkCell* cell, double* center)
    {
        // Check the input.
        if (!cell || !center)
        {
            return;
        }

        double max[3]{ 0., 0., 0. };
        int nPt = cell->GetNumberOfPoints();
        if (nPt == 0)
        {
            return;
        }

        for (int i = 0; i < nPt; i++)
        {
            double* pt = cell->GetPoints()->GetPoint(i);
            for (int j = 0; j < 3; j++)
            {
                max[j] += pt[j];
            }
        }

        for (int i = 0; i < 3; i++)
        {
            center[i] = max[i] / nPt;
        }
    }

    bool FITKGraphCommons::GetDataSetBounds(vtkDataObject* dataObj, double* bounds, bool mergeBounds)
    {
        vtkDataSet* dataSet = vtkDataSet::SafeDownCast(dataObj);
        if (!dataSet || !bounds)
        {
            return false;
        }

        double* bds = dataSet->GetBounds();

        if (mergeBounds)
        {
            // Merge min bounds.
            for (int i = 0; i < 6; i += 2)
            {
                bounds[i] = qMin(bds[i], bounds[i]);
            }

            // Merge max bounds.
            for (int i = 1; i < 6; i += 2)
            {
                bounds[i] = qMax(bds[i], bounds[i]);
            }
        }
        else
        {
            for (int i = 0; i < 6; i++)
            {
                bounds[i] = bds[i];
            }
        }

        return true;
    }

    bool FITKGraphCommons::FuzzyCompareNumericArray(vtkDataArray* arr1, vtkDataArray* arr2)
    {
        // Check th array and if they are numeric.
        if (!arr1 || !arr2)
        {
            return false;
        }

        if (!arr1->IsNumeric() || !arr2->IsNumeric())
        {
            return false;
        }

        // Check the values count and the component count.
        int nVal1 = arr1->GetNumberOfValues();
        int nVal2 = arr2->GetNumberOfValues();

        int nComp1 = arr1->GetNumberOfComponents();
        int nComp2 = arr2->GetNumberOfComponents();

        if ((nVal1 != nVal2) || (nComp1 != nComp2))
        {
            return false;
        }

        // Check the type and value.
        if (arr1->GetDataType() != arr2->GetDataType())
        {
            return false;
        }

        if ((arr1->IsA("vtkIntArray") && arr2->IsA("vtkIntArray")) ||
            (arr1->IsA("vtkIdTypeArray") && arr2->IsA("vtkIdTypeArray")))
        {
            for (int i = 0; i < nVal1; i++)
            {
                if (arr1->GetVariantValue(i).ToInt() != arr2->GetVariantValue(i).ToInt())
                {
                    return false;
                }
            }
        }
        else if (arr1->IsA("vtkFloatArray") && arr2->IsA("vtkFloatArray"))
        {
            for (int i = 0; i < nVal1; i++)
            {
                if (!FuzzyCompare(arr1->GetVariantValue(i).ToFloat(), arr2->GetVariantValue(i).ToFloat()))
                {
                    return false;
                }
            }
        }
        else if (arr1->IsA("vtkDoubleArray") && arr2->IsA("vtkDoubleArray"))
        {
            for (int i = 0; i < nVal1; i++)
            {
                if (!FuzzyCompare(arr1->GetVariantValue(i).ToDouble(), arr2->GetVariantValue(i).ToDouble()))
                {
                    return false;
                }
            }
        }
        else if (arr1->IsA("vtkUnsignedCharArray") && arr2->IsA("vtkUnsignedCharArray"))
        {
            for (int i = 0; i < nVal1; i++)
            {
                if (arr1->GetVariantValue(i).ToChar() != arr2->GetVariantValue(i).ToChar())
                {
                    return false;
                }
            }
        }
        else
        {
            return false;
        }

        return false;
    }

    int FITKGraphCommons::IntersectPlaneWithFinitePlane(double n[3], double o[3], double pOrigin[3], double px[3],
        double py[3], double x0[3], double x1[3])
    {
        int numInts = 0;
        double t, *x = x0;
        double xr0[3], xr1[3];

        // First line
        xr0[0] = pOrigin[0];
        xr0[1] = pOrigin[1];
        xr0[2] = pOrigin[2];
        xr1[0] = px[0];
        xr1[1] = px[1];
        xr1[2] = px[2];
        if (IntersectPlaneWithLine(xr0, xr1, n, o, t, x))
        {
            numInts++;
            x = x1;
        }

        // Second line
        xr1[0] = py[0];
        xr1[1] = py[1];
        xr1[2] = py[2];
        if (IntersectPlaneWithLine(xr0, xr1, n, o, t, x))
        {
            numInts++;
            x = x1;
        }
        if (numInts == 2)
        {
            return 1;
        }

        // Third line
        xr0[0] = pOrigin[0] + px[0] + py[0];
        xr0[1] = pOrigin[1] + px[1] + py[1];
        xr0[2] = pOrigin[2] + px[2] + py[2];
        if (IntersectPlaneWithLine(xr0, xr1, n, o, t, x))
        {
            numInts++;
            x = x1;
        }
        if (numInts == 2)
        {
            return 1;
        }

        // Fourth and last line
        xr1[0] = px[0];
        xr1[1] = px[1];
        xr1[2] = px[2];
        if (IntersectPlaneWithLine(xr0, xr1, n, o, t, x))
        {
            numInts++;
        }
        if (numInts == 2)
        {
            return 1;
        }

        // No intersection has occurred, or a single degenerate point
        return 0;
    }

    int FITKGraphCommons::IntersectPlaneWithLine(const double p1[3], const double p2[3], double n[3], double p0[3], double& t, double x[3])
    {
        double num, den, p21[3];
        double fabsden, fabstolerance;

        // Compute line vector
        //
        p21[0] = p2[0] - p1[0];
        p21[1] = p2[1] - p1[1];
        p21[2] = p2[2] - p1[2];

        // Compute denominator.  If ~0, line and plane are parallel.
        //
        num = vtkMath::Dot(n, p0) - (n[0] * p1[0] + n[1] * p1[1] + n[2] * p1[2]);
        den = n[0] * p21[0] + n[1] * p21[1] + n[2] * p21[2];
        //
        // If denominator with respect to numerator is "zero", then the line and
        // plane are considered parallel.
        //

        // trying to avoid an expensive call to fabs()
        if (den < 0.0)
        {
            fabsden = -den;
        }
        else
        {
            fabsden = den;
        }
        if (num < 0.0)
        {
            fabstolerance = -num * PLANE_TOL;
        }
        else
        {
            fabstolerance = num * PLANE_TOL;
        }
        if (fabsden <= fabstolerance)
        {
            t = VTK_DOUBLE_MAX;
            return -1;
        }

        // valid intersection
        t = num / den;

        x[0] = p1[0] + t * p21[0];
        x[1] = p1[1] + t * p21[1];
        x[2] = p1[2] + t * p21[2];

        if (t >= 0.0 && t <= 1.0)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

    bool FITKGraphCommons::CreateLineStipTexture(vtkDataObject* dataSet, vtkTexture* texture, int lineStipplePattern, int lineStippleRepeat)
    {
        vtkDataSet* input = vtkDataSet::SafeDownCast(dataSet);
        if (!input || !texture)
        {
            return false;
        }

        // Down cast to poly data.
        //vtkPolyData* polyData = vtkPolyData::SafeDownCast(dataSet);
        //if (!polyData)
        //{
        //    return false;
        //}

        vtkSmartPointer<vtkDoubleArray> tcoords = vtkSmartPointer<vtkDoubleArray>::New();
        vtkSmartPointer<vtkImageData> image = vtkSmartPointer<vtkImageData>::New();

        // Create texture.
        int dimension = 16 * lineStippleRepeat;

        image->SetDimensions(dimension, 1, 1);
        image->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
        image->SetExtent(0, dimension - 1, 0, 0, 0, 0);

        unsigned char* pixel = static_cast<unsigned char*>(image->GetScalarPointer());
        unsigned char on = 255;
        unsigned char off = 0;

        for (int i = 0; i < 16; ++i)
        {
            unsigned int mask = (1 << i);
            unsigned int bit = (lineStipplePattern & mask) >> i;
            unsigned char value = static_cast<unsigned char>(bit);
            if (value == 0)
            {
                for (int j = 0; j < lineStippleRepeat; ++j)
                {
                    *pixel = on;
                    *(pixel + 1) = on;
                    *(pixel + 2) = on;
                    *(pixel + 3) = off;
                    pixel += 4;
                }
            }
            else
            {
                for (int j = 0; j < lineStippleRepeat; ++j)
                {
                    *pixel = on;
                    *(pixel + 1) = on;
                    *(pixel + 2) = on;
                    *(pixel + 3) = on;
                    pixel += 4;
                }
            }
        }

        // Create texture coordnates.
        tcoords->SetNumberOfComponents(1);
        tcoords->SetNumberOfTuples(input->GetNumberOfPoints());

        for (int i = 0; i < input->GetNumberOfPoints(); ++i)
        {
            double value = static_cast<double>(i) * .5;
            tcoords->SetTypedTuple(i, &value);
        }

        input->GetPointData()->SetTCoords(tcoords);
        texture->SetInputData(image);
        texture->InterpolateOff();
        texture->RepeatOn();

        return true;
    }

    void FITKGraphCommons::AddStippleLine(vtkUnstructuredGrid* ugrid, double* start, double* end, int nSeg)
    {
        if (!ugrid || !start || !end)
        {
            return;
        }

        // Get the points of grid data.
        vtkPoints* points = ugrid->GetPoints();
        if (!points)
        {
            return;
        }

        // Check the number of segment.
        nSeg = nSeg < 2 ? 100 : nSeg;

        // Create dash line.
        double length = sqrt(pow(end[0] - start[0], 2) + pow(end[1] - start[1], 2) + pow(end[2] - start[2], 2));

        // The points' interval distance.
        int nPts = nSeg + 1;
        double delta = length / nSeg;

        // Get the line direction.
        double dire[3]{ 0., 0., 0. };
        if (!FuzzyCompare(length, 0))
        {
            for (int i = 0; i < 3; i++)
            {
                dire[i] = (end[i] - start[i]) / length;
            }
        }

        // Create points.
        int startIndex = points->InsertNextPoint(start);
        for (int i = 1; i <= nPts; i++)
        {
            double pt[3] = { start[0] + dire[0] * i * delta,
                start[1] + dire[1] * i * delta,
                start[2] + dire[2] * i * delta };
            points->InsertNextPoint(pt);
        }

        // Create line segments.
        int nCell = nPts / 2;
        for (int i = 0; i < nCell; i++)
        {
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(i * 2 + startIndex);
            cell->InsertNextId(i * 2 + 1 + startIndex);
            ugrid->InsertNextCell(VTKCellType::VTK_LINE, cell);
        }
    }

    void FITKGraphCommons::ProjectPointVector(double* pOrigin, double* pNormal, double* inPoint, double* outVector)
    {
        if (!pOrigin || !pNormal || !inPoint || !outVector)
        {
            return;
        }

        // Get the projected point.
        double outPoint[3]{ 0., 0., 0. };
        vtkPlane::ProjectPoint(inPoint, pOrigin, pNormal, outPoint);

        // Get the direction with two points.
        double vec3[3]{ outPoint[0] - pOrigin[0], outPoint[1] - pOrigin[1], outPoint[2] - pOrigin[2] };

        // Normalize.
        vtkMath::Normalize(vec3);

        for (int i = 0; i < 3; i++)
        {
            outVector[i] = vec3[i];
        }
    }

    void FITKGraphCommons::GetPlaneNormal(double* origin, double* direc1, double* direc2, double* normal)
    {
        if (!origin || !direc1 || !direc2 || !normal)
        {
            return;
        }

        // Create points.
        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
        points->InsertNextPoint(origin);

        double fac = 1000.;
        points->InsertNextPoint(origin[0] + direc1[0] * fac, origin[1] + direc1[1] * fac, origin[2] + direc1[2] * fac);
        points->InsertNextPoint(origin[0] + direc2[0] * fac, origin[1] + direc2[1] * fac, origin[2] + direc2[2] * fac);

        // Get the plane normal.
        vtkPolygon::ComputeNormal(points, normal);
    }

    void FITKGraphCommons::InitializeMaskPoints(vtkMaskPoints* & maskPoints, vtkDataSet* input, int number)
    {
        // Create the filter.
        if (!maskPoints)
        {
            maskPoints = vtkMaskPoints::New();
        }

        // Initialize.
        maskPoints->SetMaximumNumberOfPoints(number);
        maskPoints->SetRandomMode(false);
        
        // Initialize input.
        if (input)
        {
            maskPoints->SetInputData(input);
        }
    }

    void FITKGraphCommons::InitializeMaskPoints(vtkMaskPoints* & maskPoints, vtkAlgorithmOutput* input, int number)
    {
        // Create the filter.
        if (!maskPoints)
        {
            maskPoints = vtkMaskPoints::New();
        }

        // Initialize.
        maskPoints->SetMaximumNumberOfPoints(number);
        maskPoints->SetRandomMode(true);

        // Initialize input connection.
        if (input)
        {
            maskPoints->SetInputConnection(input);
        }
    }

    bool FITKGraphCommons::GetMouseWorldPositionOnPlane(vtkRenderer* renderer, int* mousePos, double* pNor, double* pPos, double* mouseWorldPos)
    {
        if (!renderer)
        {
            return false;
        }

        // Get the camera information.
        vtkCamera* camera = renderer->GetActiveCamera();
        if (!camera)
        {
            return false;
        }

        double pos3[3], viewFocus3[3], mosueWorldPoint4[4];
        camera->GetFocalPoint(viewFocus3);
        camera->GetPosition(pos3);

        vtkInteractorObserver::ComputeDisplayToWorld(
            renderer, mousePos[0], mousePos[1], 0, mosueWorldPoint4);

        // Get the view direction.
        double dirCam3[3]{ 0., 0., 0. };
        if (camera->GetParallelProjection())
        {
            camera->GetViewPlaneNormal(dirCam3);
        }
        else
        {
            for (int i = 0; i < 3; i++)
            {
                dirCam3[i] = mosueWorldPoint4[i] - pos3[i];
            }
        }

        // Get the second point with the direction.
        double lenFac = 1000.;
        double newPoint2[3]{ mosueWorldPoint4[0] + dirCam3[0] * lenFac, mosueWorldPoint4[1] + dirCam3[1] * lenFac,
            mosueWorldPoint4[2] + dirCam3[2] * lenFac };

        // Get the cross point of the line and the plane.
        double t = 0;
        int flag = IntersectPlaneWithLine(mosueWorldPoint4, newPoint2, pNor, pPos, t, mouseWorldPos);

        return flag != -1;
    }

    bool FITKGraphCommons::SnipToByPixel(vtkRenderer* iRen, vtkPointSet* iDataSet, double iDistForPoint, double iDistForCell,
        double* iPos, double* oPos, double & oPxDist)
    {
        if (!iRen || !iDataSet)
        {
            return false;
        }

        // Check the distance.
        iDistForCell = iDistForCell < 0. ? 0. : iDistForCell;

        if (!iDataSet->GetNumberOfPoints() || !iDataSet->GetNumberOfCells())
        {
            return false;
        }

        // Get the cell locator.
        vtkAbstractCellLocator* cellLoc = iDataSet->GetCellLocator();
        if (!cellLoc)
        {
            return false;
        }

        double posFound3[3]{ 0., 0., 0. };
        double weight3[3]{ 0., 0., 0. };
        vtkIdType cellId = 0;
        int subId = 0;
        double dist = 0.;

        cellLoc->FindClosestPoint(iPos, posFound3, vtkSmartPointer<vtkGenericCell>::New(), cellId, subId, dist);

        // Check the pixel distance.
        double mPosInput[3], mPosOutput[3];
        vtkInteractorObserver::ComputeWorldToDisplay(iRen, iPos[0], iPos[1], iPos[2], mPosInput);
        vtkInteractorObserver::ComputeWorldToDisplay(iRen, posFound3[0], posFound3[1], posFound3[2], mPosOutput);

        dist = sqrt(vtkMath::Distance2BetweenPoints(mPosInput, mPosOutput));
        if (dist > iDistForCell)
        {
            return false;
        }

        oPxDist = dist;

        // Save the position.
        for (int i = 0; i < 3; i++)
        {
            oPos[i] = posFound3[i];
        }

        if (iDistForPoint < 0.)
        {
            return true;
        }

        // Get the nearest point on the cell.
        vtkCell* cell = iDataSet->GetCell(cellId);
        if (!cell)
        {
            return true;
        }

        int nearestI = -1;
        double distCellPtI = VTK_DOUBLE_MAX;
        vtkPoints* cellPts = cell->GetPoints();
        for (int i = 0; i < cellPts->GetNumberOfPoints(); i++)
        {
            double* pos = cellPts->GetPoint(i);
            double distCellPt = sqrt(vtkMath::Distance2BetweenPoints(iPos, pos));
            if (distCellPt < VTK_DOUBLE_MAX)
            {
                nearestI = i;
                distCellPtI = distCellPt;
            }
        }

        // Check the pixel distance from the cell point to the input position.
        double* posNearestCellPt = cellPts->GetPoint(distCellPtI);
        vtkInteractorObserver::ComputeWorldToDisplay(iRen, posNearestCellPt[0], posNearestCellPt[1], posNearestCellPt[2], mPosOutput);

        dist = sqrt(vtkMath::Distance2BetweenPoints(mPosInput, mPosOutput));
        oPxDist = dist;

        if (dist > iDistForPoint)
        {
            return true;
        }

        // Save the position.
        for (int i = 0; i < 3; i++)
        {
            oPos[i] = posNearestCellPt[i];
        }

        return true;
    }
}   // namespace Exchange